package edu.hadassah.store.policy;



import java.beans.PropertyChangeListener;

import edu.hadassah.store.employee.IEmployee;
import edu.hadassah.store.management.IStoreManagement;
import edu.hadassah.store.management.NotificationManager;
import edu.hadassah.store.order.IOrder;
import edu.hadassah.store.order.IOrderInfo;
import edu.hadassah.store.uniqueid.IUniqueId;

/**
 * 
 */
/**
 * @author persikov
 *
 */
public aspect OrderOperationPolicy {

	/**
	 * @param storeManagement
	 * @param orderId
	 */
	pointcut removeOrder(IStoreManagement storeManagement, IUniqueId orderId) : target(storeManagement) && args(orderId) && call(boolean IStoreManagement+.removeOrder(IUniqueId));

	/**
	 * @param storeManagement
	 * @param orderId
	 * @return
	 */
	boolean around (IStoreManagement storeManagement, IUniqueId orderId):removeOrder(storeManagement, orderId){
		Trace.traceEntry(thisJoinPoint);
		// we have to get order before it was removed
		boolean removed = proceed(storeManagement, orderId);
		if (removed){
			removedEntityHandler(storeManagement, orderId);
		}
		return removed;
	}

	/**
	 * @param storeManagement
	 * @param orderId
	 */
	private void removedEntityHandler(IStoreManagement storeManagement, IUniqueId orderId) {
		for ( IEmployee employee : storeManagement.getEmpoyeeRepository()){
			if ( employee instanceof PropertyChangeListener){
				NotificationManager.getInstance().removeOrderListener(orderId, (PropertyChangeListener)employee);
			}
		}
	}

	/**
	 * @param storeManagement
	 */
	pointcut addOrder(IStoreManagement storeManagement) : target(storeManagement) && call(IUniqueId IStoreManagement+.addOrder(IOrderInfo));
	/**
	 * @param storeManagement
	 * @param orderId
	 */
	after (IStoreManagement storeManagement) returning(IUniqueId orderId) : addOrder(storeManagement){
		Trace.traceEntry(thisJoinPoint);
		if (orderId != null){
			addEntityHandler(storeManagement, orderId);
		}
	}

	/**
	 * @param storeManagement
	 * @param order
	 */
	private void addEntityHandler(IStoreManagement storeManagement, IUniqueId orderId) {
		for (IEmployee employee : storeManagement.getEmpoyeeRepository()) {
			if ( employee instanceof PropertyChangeListener){
				NotificationManager.getInstance().addOrderListener(orderId, (PropertyChangeListener)employee);
			}
		}

	}


	/**
	 * @param storeManagement
	 * @param orderId
	 */
	pointcut updateOrder(IStoreManagement storeManagement, IUniqueId orderId, IOrderInfo updateOrder) : target(storeManagement) && args(orderId, updateOrder) && call(boolean IStoreManagement+.updateOrder(IUniqueId, IOrderInfo));

	/**
	 * @param storeManagement
	 * @param orderId
	 * @return
	 */
	boolean around (IStoreManagement storeManagement, IUniqueId orderId, IOrderInfo updateOrder) :updateOrder(storeManagement, orderId, updateOrder){
		Trace.traceEntry(thisJoinPoint);
		// we have to get order before it was updated
		IOrder order = storeManagement.getOrder(orderId);
		// if order not found we have nothing to do
		if ( order == null){
			return false;
		}
		// we will need this memento for employee update
		IOrderInfo oldOrderMemento =  order.toMemento();
		boolean updated = proceed(storeManagement, orderId, updateOrder);
		if (updated){
			entityUpdateHandler(storeManagement, orderId, oldOrderMemento, updateOrder);
		}
		return updated;
	}

	/**
	 * @param storeManagement
	 * @param orderId
	 */
	private void entityUpdateHandler(IStoreManagement storeManagement, IUniqueId orderId, IOrderInfo oldOrderMemento, IOrderInfo updateOrder) {
		NotificationManager.getInstance().fireOrderChange(orderId, oldOrderMemento, updateOrder);
	}

	pointcut getRepository():call (Iterable<*> IStoreManagement+.get*Repository());
	pointcut canGetRepository(): within(edu.hadassah.store.policy.*);

	before() : getRepository() && !canGetRepository(){
		String errMsg = "Access to the " + thisJoinPoint.getSignature().toLongString() + " is forbiden";
		throw new RuntimeException(errMsg  , new IllegalAccessException(errMsg + " from " + thisJoinPoint.getSourceLocation().getWithinType().toString()));
	}
}
